#include "PrimitivesCounter.h"

PrimitivesCounter::PrimitivesCounter(int* imagePixels, int imageWidth)
{
	this->pixels = imagePixels;
	this->imageWidth = imageWidth;
}

PrimitivesCounter::~PrimitivesCounter(void)
{
}

int PrimitivesCounter::countPrimitives(PrimitiveDirection direction, unordered_map <int, unordered_map <int, int>*> *primitives1,
									   unordered_map <int, unordered_map <int, int>*> *primitives2, int xStart, int xEnd, int yStart, int yEnd)
{
	switch(direction)
	{
	case PrimitiveDirection::Horizontal:
		return countPrimitivesHorizontal(primitives1, primitives2, xStart, xEnd, yStart, yEnd);
	case PrimitiveDirection::Vertical:
		return countPrimitivesVertical(primitives1, primitives2, xStart, xEnd, yStart, yEnd);
	case PrimitiveDirection::Both:
		return countPrimitivesBothDirections(primitives1, primitives2, xStart, xEnd, yStart, yEnd);
	default:
		// nigdy, jakis error
		return -1;
	}
}

void PrimitivesCounter::destroyPrimitives(unordered_map <int, unordered_map <int, int>*> *primitives)
{
	for ( auto it = primitives->begin(); it != primitives->end(); ++it )
	{
		delete(it->second);	// destroying the underlaying maps
	}
}

int PrimitivesCounter::countPrimitivesHorizontal(unordered_map <int, unordered_map <int, int>*> *primitives1,
												 unordered_map <int, unordered_map <int, int>*> *primitives2, int xStart, int xEnd, int yStart, int yEnd)
{	
	int primitivesCount = 0;
	long color;
	int primitiveStartIndex;
	int primitiveLength;

	for(int y=yStart; y < yEnd; y++)
	{
		color = this->pixels[y*this->imageWidth+xStart];	// taking the first pixel so that we can compare
		primitiveStartIndex = xStart;

		int x;
		for(x=xStart+1; x < xEnd; x++)
		{
			if(this->pixels[y*this->imageWidth+x] != color)	// we found another primitive
			{
				primitiveLength = x-primitiveStartIndex;
				addPrimitive(primitives1, color, primitiveLength);
				addPrimitive(primitives2, primitiveLength, color);				
  
				color = pixels[y*this->imageWidth+x];
				primitiveStartIndex = x;
				primitivesCount++;
			}
		}
		
		// end of the line, end of the primitive
		primitiveLength = x-primitiveStartIndex;
		addPrimitive(primitives1, color, primitiveLength);
		addPrimitive(primitives2, primitiveLength, color);		
		primitivesCount++;
	}

	return primitivesCount;
}

int PrimitivesCounter::countPrimitivesVertical(unordered_map <int, unordered_map <int, int>*> *primitives1,
											   unordered_map <int, unordered_map <int, int>*> *primitives2, int xStart, int xEnd, int yStart, int yEnd)
{
	int primitivesCount = 0;
	long color;
	int primitiveStartIndex;
	int primitiveLength;

	for(int x=xStart; x < xEnd; x++)
	{
		color = this->pixels[yStart*this->imageWidth+x];	// taking the first pixel so that we can compare
		primitiveStartIndex = yStart;

		int y;
		for(y=yStart+1; y < yEnd; y++)
		{
			if(this->pixels[y*this->imageWidth+x] != color)	// we found another primitive
			{
				primitiveLength = y-primitiveStartIndex;
				addPrimitive(primitives1, color, primitiveLength);
				addPrimitive(primitives2, primitiveLength, color);

				color = pixels[y*this->imageWidth+x];
				primitiveStartIndex = y;
				primitivesCount++;
			}
		}

		// end of the line, end of the primitive
		primitiveLength = y-primitiveStartIndex;
		addPrimitive(primitives1, color, primitiveLength);
		addPrimitive(primitives2, primitiveLength, color);
		primitivesCount++;
	}

	return primitivesCount;
}

int PrimitivesCounter::countPrimitivesBothDirections(unordered_map <int, unordered_map <int, int>*> *primitives1,
													 unordered_map <int, unordered_map <int, int>*> *primitives2, int xStart, int xEnd, int yStart, int yEnd)
{
	int primitivesCount;
	primitivesCount = countPrimitivesHorizontal(primitives1, primitives2, xStart, xEnd, yStart, yEnd);
	primitivesCount += countPrimitivesVertical(primitives1, primitives2, xStart, xEnd, yStart, yEnd);
	return primitivesCount;
}

void PrimitivesCounter::addPrimitive(unordered_map <int, unordered_map <int, int>*> *primitives, int key1, int key2)
{
	ensureMapLevel1(primitives, key1);
	ensureMapLevel2((*primitives)[key1], key2);

	(*primitives)[key1]->at(key2)++;	// one more primitive of that type
}


void PrimitivesCounter::ensureMapLevel1(unordered_map <int, unordered_map <int, int>*> *level1Map, int level1Key)
{
	unordered_map <int, unordered_map <int, int>*>::const_iterator searchedKey1;
	searchedKey1 = level1Map->find(level1Key);

	if(searchedKey1 == level1Map->end())	// the key was not found
	{
		unordered_map <int, int> *level2Map = new unordered_map <int, int>();
		pair<int, unordered_map <int, int>*> keyValuePair (level1Key,level2Map);
		level1Map->insert(keyValuePair);
	}
}

void PrimitivesCounter::ensureMapLevel2(unordered_map <int, int> *level2Map, int level2Key)
{
	unordered_map <int, int>::const_iterator searchedKey2;
	searchedKey2 = level2Map->find(level2Key);

	if(searchedKey2 == level2Map->end())	// the key was not found
	{
		pair<int, int> keyValuePair (level2Key, 0);
		level2Map->insert(keyValuePair);
	}
}
